package com.recharge.service.httpInterFace;

import com.alibaba.fastjson.JSONObject;
import com.recharge.common.result.ResultDO;
import com.recharge.common.result.ResultSupport;
import com.recharge.common.util.Constant;
import com.recharge.common.util.MD5Util;
import com.recharge.common.util.TemplateUtils;
import com.recharge.common.util.http.HttpClientUtil;
import com.recharge.domain.query.OrderStatusQueryDO;
import com.recharge.domain.vo.GoodsChannelDO;
import com.recharge.domain.vo.ProviderApiDO;
import com.recharge.domain.vo.ProviderCallbackDO;
import com.recharge.service.ProviderApiService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <h1>上游请求(小马系统)</h1>
 *
 * @author kui
 * @date 2020.10.24
 */
@Service
public class XiaomaHTTPImpl implements ProviderHTTPService {

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    @Autowired
    ProviderApiService providerApiService;

    @Override
    public ResultDO commitOrder(GoodsChannelDO domain) {
        if (TemplateUtils.isEmpty(domain.getProviderSerialNumber())) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单号为空 %s", domain.toString())
                    , "订单号为空"
                    , null);
        }

        ProviderCallbackDO callbackDO = new ProviderCallbackDO();
        callbackDO.setSerialNumber(domain.getProviderSerialNumber());
        callbackDO.setProviderId(domain.getProviderId());
        callbackDO.setOrderNo(domain.getOrderNo());
        callbackDO.setStatus(Constant.CALL_BACK_PROCESS);
        ResultDO result = providerApiService.addCallback(callbackDO);
        if (!result.isSuccess()) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单%s 添加存根信息失败", domain.getProviderSerialNumber())
                    , "添加回调存根信息失败"
                    , null);
        }

        //请求金额(元)
        Long amount = domain.getAmount() / 1000;
        //商品编号
//        String goodsNo = domain.getProviderGoodsNo();
        //平台流水号
        String serialNumber = domain.getProviderSerialNumber();
        //充值号码
        String rechargeNumber = domain.getRechargeNumber();
        //供应商ID
        String userId = domain.getUserId();
        //供应商密钥
        String privateKey = domain.getPrivateKey();
        //回调地址
        String notifyurl = domain.getCallback();
        // 设置日期格式  加密签名
        String formatTime = System.currentTimeMillis() + "";
        //签名
        String afterSign = "";
        try {
            String beforeSign =
                    String.format("username=%s&orderno=%s&cardno=%s&cost=%s&notifyurl=%s&timestamp=%s&key=%s",
                            userId, serialNumber, rechargeNumber, amount, notifyurl, formatTime, privateKey);

            logger.debug("before_sign: " + beforeSign);
            afterSign = MD5Util.md5(beforeSign);
        } catch (Exception e) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 渠道商ID: %s HTTP请求参数构建失败", domain.getProviderId())
                    , "HTTP请求参数构建失败"
                    , null);
        }
        //请求参数
//        String requestParam = String.format("cost=%s&timestamp=%s&orderno=%s&cardno=%s&username=%s&flag=1&notifyurl=%s&sign=%s",
//                amount, formatTime, serialNumber, rechargeNumber, userId, notifyurl, afterSign);

        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("cost", amount.toString());
        requestParamMap.put("timestamp", formatTime);
        requestParamMap.put("orderno", serialNumber);
        requestParamMap.put("cardno", rechargeNumber);
        requestParamMap.put("username", userId);
        requestParamMap.put("sign", afterSign);
        requestParamMap.put("flag", "1");
        requestParamMap.put("notifyurl", notifyurl);

        // 发送请求
        logger.warn(String.format("订单提交 >> 发送GET请求(订单号%s): %s?%s", domain.getProviderSerialNumber(), domain.getRequestUrl(), requestParamMap.toString()));
        String sendResult = HttpClientUtil.doGet(domain.getRequestUrl(), requestParamMap);
        switch (sendResult) {
            case "500":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求失败", domain.getProviderSerialNumber())
                        , "HTTP请求失败"
                        , sendResult);
            case "504":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求超时", domain.getProviderSerialNumber())
                        , "HTTP请求超时"
                        , sendResult);
            case "":
                return returnFail(false
                        , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                        , String.format("订单提交 >> 订单号 %s -> 订单提交供货商HTTP请求没有返回信息", domain.getProviderSerialNumber())
                        , "订单提交没有返回信息"
                        , sendResult);
        }
        logger.warn(String.format("订单提交 >> 请求返回信息: 订单号%s -> %s", domain.getProviderSerialNumber(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("订单提交 >> 请求返回结果解析json失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 订单提交没有返回信息", domain.getProviderSerialNumber())
                    , "没有返回信息"
                    , sendResult);
        }

        String code = null;
        String desc = "";
        try {
            code = resultJson.get("rtncode").toString();
            desc = resultJson.get("rtncontent").toString();
        } catch (Exception e) {
            logger.debug(String.format("获取返回信息失败: 订单号%s -> %s", domain.getProviderSerialNumber(), sendResult));
        }
        if (code == null) {
            return returnFail(false
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 没有获取到返回状态码", domain.getProviderSerialNumber())
                    , "订单提交没有返回状态码"
                    , sendResult);
        }

        // 状态码: 下单失败
        String[] array = {"-1", "-2", "-4", "-5", "-6", "-7", "-9"};

        // 状态码: 未确认
        String[] array2 = {"-3", "-8"};

        if ("0".equals(code)) { //成功
            logger.debug(String.format("订单提交 >> 订单号 %s -> 订单提交成功|%s %s", domain.getProviderSerialNumber(), code, desc));
            result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.BUSINESS_RECHARGE_RETURN_STATUS_SUCCESS);
            result.setModel(ResultDO.SECOND_MODEL_KEY, "");
            return result;
        } else if (Arrays.asList(array).contains(code)) {//失败
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_FAIL
                    , String.format("订单提交 >> 订单号 %s -> 订单提交失败|%s %s", domain.getProviderSerialNumber(), code, desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else if (Arrays.asList(array2).contains(code)) {//未确认
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 订单未确认|%s %s", domain.getProviderSerialNumber(), code, desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        } else {//未知
            return returnFail(true
                    , Constant.BUSINESS_RECHARGE_RETURN_STATUS_OUT_TIME
                    , String.format("订单提交 >> 订单号 %s -> 订单未知|%s %s", domain.getProviderSerialNumber(), code, desc)
                    , String.format("%s %s", desc, code)
                    , sendResult);
        }
    }

    @Override
    public ResultDO backOrder(ProviderApiDO domain, String paramStr, String bodyStr) {
        ResultDO result = new ResultSupport();
        JSONObject jsonObject = null;
        try {
            jsonObject = JSONObject.parseObject(paramStr);
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析后数据: " + jsonObject.toJSONString());
        } catch (Exception e) {
            logger.debug("订单异步回调 >> " + domain.getProviderNo() + "解析json失败:" + paramStr);
        }
        if (jsonObject == null) {
            logger.debug("订单异步回调 >> 收到异步回调,但是没有回调信息");
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        //上游流水号
        String orderNo = jsonObject.getString("inorder");
        //平台流水号
        String serialNum = jsonObject.getString("orderno");
        //充值账号
        String username = jsonObject.getString("username");
        //充值号码
        String cardno = jsonObject.getString("cardno");
        //该笔订单的实际扣款金额
        String saleprice = jsonObject.getString("saleprice");
        //充值凭证
        String voutcher = jsonObject.getString("orderinfo");
        //回调签名
        String returnSign = jsonObject.getString("sign");
        //状态
        String status = jsonObject.getString("flag");
        //加密前签名
        String beforeVerifySign =
                String.format("orderno=%s&inorder=%s&username=%s&cardno=%s&saleprice=%s&flag=%s&key=%s"
                        , serialNum, orderNo, username, cardno, saleprice, status, domain.getPrivateKey());

        String afterVerifySign = "";
        try {
            afterVerifySign = MD5Util.md5(beforeVerifySign, 32);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        logger.debug("签名验证: after >>"+afterVerifySign);
        if (!afterVerifySign.equals(returnSign)) {
            logger.debug("订单异步回调 >> 签名校验: 加密前 >>" + beforeVerifySign);
            logger.debug(String.format("订单异步回调 >> 订单:%s 签名校验不一致 {本地签名:%s 返回签名:%s }", serialNum, afterVerifySign, returnSign));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            return result;
        }

        //判断订单状态
        if (status.equals("2")) {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:%s 成功 status: %s", serialNum, status));
            result = providerApiService.updateCallback(serialNum, voutcher, orderNo, Constant.CALL_BACK_SUCCESS);
        } else if (status.equals("3")) {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:%s 失败 status: %s", serialNum, status));
            result = providerApiService.updateCallback(serialNum, "", orderNo, Constant.CALL_BACK_FAIL);
        } else {
            logger.debug(String.format("订单异步回调 >> 订单异步返回:%s 未确认 status: %s", serialNum, status));
            result = providerApiService.updateCallback(serialNum, "", orderNo, Constant.CALL_BACK_NUKNOW);
        }
        if (!result.isSuccess()) {
            result.setModel(ResultDO.FIRST_MODEL_KEY, "fail");
            logger.debug("订单异步回调 >> 修改订单(" + serialNum + ")状态失败");
            return result;
        }

        result.setModel(ResultDO.FIRST_MODEL_KEY, "success");
        return result;

    }

    @Override
    public ResultDO queryOrder(OrderStatusQueryDO domain) {
        ResultDO result = new ResultSupport();

        String sign = "";
        try {
            sign = MD5Util.md5(String.format("username=%s&orderno=%s&key=%s", domain.getUserId(), domain.getSerialNo(), domain.getPrivateKey()));
        } catch (Exception e) {
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "签名加密失败");
            logger.debug("查询订单 >> 签名加密失败" + e.getMessage());
            return result;
        }

        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("serialno", domain.getSerialNo());
        requestParamMap.put("userId", domain.getUserId());
        requestParamMap.put("sitn", sign);
        // 发送请求
        logger.debug(String.format("查询订单 >> 发送GET请求(订单号%s): url %s , param %s", domain.getSerialNo(), domain.getOrderFindUrl(), requestParamMap));
        String sendResult = HttpClientUtil.doGet(domain.getOrderFindUrl(), requestParamMap);
        logger.debug(String.format("查询订单 >> 请求返回信息: 订单号%s -> %s", domain.getSerialNo(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 订单%s 请求返回结果解析json失败: %s", domain.getSerialNo(), sendResult));
        }
        if (resultJson.size() == 0) {
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到返回信息");
            logger.debug("查询订单 >> 订单(" + domain.getSerialNo() + ")未获取到返回信息");
            return result;
        }

        String code = null;
        String status = null;
        String voucher = "";
        try {
            code = resultJson.getString("flag");
            if (resultJson.get("orderinfo") != null) {
                voucher = resultJson.getString("orderinfo");
            }
            status = resultJson.getString("rtncode");
        } catch (Exception e) {
            logger.debug(String.format("查询订单 >> 获取返回结果集失败: 订单号%s -> %s", domain.getSerialNo(), sendResult));
        }
        if (TemplateUtils.isEmpty(status) || TemplateUtils.isEmpty(code)) {
            result.setSuccess(false);
            result.setModel(ResultDO.THIRD_MODEL_KEY, "未获取到订单状态码");
            logger.debug(String.format("查询订单 >> 未获取到订单(%s)状态码", domain.getSerialNo()));
            return result;
        }

        switch (status) {
            case "0"://成功
                if (code.equals("1")) {
                    logger.debug(String.format("查询订单 >> 订单号 %s -> 订单处理中|%s", domain.getSerialNo(), status));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                } else if (code.equals("2")) {
                    logger.debug(String.format("查询订单 >> 订单号 %s -> 订单成功|%s", domain.getSerialNo(), status));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_SUCCESS);
                    result.setModel(ResultDO.SECOND_MODEL_KEY, voucher);
                } else if (code.equals("3")) {
                    logger.debug(String.format("查询订单 >> 订单号 %s -> 订单失败|%s", domain.getSerialNo(), status));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_FAIL);
                } else {
                    logger.debug(String.format("查询订单 >> 订单号 %s -> 订单未知|%s", domain.getSerialNo(), status));
                    result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                }
//                logger.debug(String.format("查询订单 >> 订单号 %s -> 订单成功|%s", domain.getSerialNo()));
                return result;
            case "-1":
                result.setSuccess(false);
                logger.debug(String.format("查询订单 >> 订单号 %s -> 账号错误|%s", domain.getSerialNo(), status));
//                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                return result;
            case "-8":
                result.setSuccess(false);
                logger.debug(String.format("查询订单 >> 订单号 %s -> 订单号错误|%s", domain.getSerialNo(), status));
//                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                return result;
            case "-9":
                result.setSuccess(false);
                logger.debug(String.format("查询订单 >> 订单号 %s -> 签名错误|%s", domain.getSerialNo(), status));
//                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                return result;
            default:
                result.setSuccess(false);
                logger.debug(String.format("查询订单 >> 订单号 %s -> 未知状态|%s", domain.getSerialNo(), status));
//                result.setModel(ResultDO.FIRST_MODEL_KEY, Constant.CALL_BACK_NUKNOW);
                return result;
        }

    }

    @Override
    public ResultDO queryBalance(ProviderApiDO domain) {
        ResultDO result = new ResultSupport();
        if (domain.getPrivateKey() == null || domain.getUserId() == null) {
            result.setSuccess(false);
            logger.debug("余额查询 >> 没有查询到供货商信息配置");
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        Map<String, String> requestParamMap = new HashMap<>();
        requestParamMap.put("username", domain.getUserId());
        String sign = "";
        try {
            sign = MD5Util.md5(String.format("username=%s&key=%s", domain.getUserId(), domain.getPrivateKey()));
        } catch (Exception e) {
            logger.debug("余额查询 >> 供应商账号: "+domain.getUserId()+" 签名加密失败" + e.getMessage());
            result.setSuccess(false);
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        requestParamMap.put("sign", sign);

        // 发送请求
        String sendResult = HttpClientUtil.doGet(domain.getBalanceFindUrl(), requestParamMap);
        logger.debug(String.format("余额查询 >> 余额查询返回信息: 供应商账号 %s -> %s", domain.getUserId(), sendResult));

        // 解析请求返回结果转换为json对象
        JSONObject resultJson = new JSONObject();
        try {
            resultJson = JSONObject.parseObject(sendResult);
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 请求返回结果解析JSON失败: %s", sendResult));
        }
        if (resultJson.size() == 0) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号: %s -> 余额查询没有返回信息", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }
        String code = null;
        String balance = null;
        try {
            code = resultJson.getString("rtncode");
            balance = resultJson.getString("balance");
        } catch (Exception e) {
            logger.debug(String.format("余额查询 >> 获取返回信息失败: 供应商账号%s -> %s", domain.getUserId(), sendResult));
        }
        if (code == null) {
            result.setSuccess(false);
            logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询失败,没有返回状态码", domain.getUserId()));
            result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
            return result;
        }

        switch (code) {
            case "0":
                result.setModel(ResultDO.FIRST_MODEL_KEY, balance);
                return result;
            case "-1":
//                result.setSuccess(false);
                logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询失败 | %s", domain.getUserId(), code));
                result.setModel(ResultDO.FIRST_MODEL_KEY, "账号错误");
                return result;
            case "-9":
//                result.setSuccess(false);
                logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询签名失败 | %s", domain.getUserId(), code));
                result.setModel(ResultDO.FIRST_MODEL_KEY, "签名失败");
                return result;
            default:
//                result.setSuccess(false);
                logger.debug(String.format("余额查询 >> 供应商账号 %s -> 余额查询 未知 | %s", domain.getUserId(), code));
                result.setModel(ResultDO.FIRST_MODEL_KEY, "查询失败");
                return result;
        }

    }

    /**
     * <h2>提交订单返回失败</h2>
     *
     * @param success    是否处理成功
     * @param code       订单状态
     * @param logMessage 失败信息
     * @param message    供货摘要
     * @param entity     返回信息字符串
     * @return FIRST_MODEL_KEY: 订单状态
     * SECOND_MODEL_KEY: 供货摘要
     * THIRD_MODEL_KEY: 上游返回信息字符串
     */
    private ResultDO returnFail(boolean success, int code, String logMessage, String message, String entity) {
        ResultDO result = new ResultSupport();
        result.setSuccess(success);
        result.setModel(ResultDO.FIRST_MODEL_KEY, code);
        result.setModel(ResultDO.SECOND_MODEL_KEY, message);
        result.setModel(ResultDO.THIRD_MODEL_KEY, entity);
        logger.debug(logMessage);
        return result;
    }

}
